Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@homer0/simple-config
Advanced tools
A very simple configuration management for your projects. It takes care of loading, activating, switching and merging configuration files.
⚠️ This package is only for Node.
Let's say your app tree looks like this:
myApp/
├── config/
│ ├── development.js
│ └── production.js
└── app/
└── index.js
And you execute your app with something like:
node app
If you don't have anything managing the configurations, you would probably have something like this:
const config = (
await (process.NODE_ENV === 'production'
? import('../config/production')
: import('../config/development'))
).default;
And the, development.js
probably imports production
, and has some deep merge logic, to avoid re writing the whole thing.
Maybe is different, but we can simplify it a little bit with simpleConfig
:
import { simpleConfig } from '@homer0/simple-config';
const config = simpleConfig({
filenameFormat: '[name].js',
path: 'config',
});
// ... sometime later, because there's not top-level await yet.
await config.load(process.NODE_ENV || 'development');
Now you can go to development
and just add a key extends
with the value production
, and simpleConfig
will take care of loading the file you need, and deep merge the one for development.
The reason the default options needed to be changed was because:
filenameFormat
: To avoid changing the name of the files, since the service looks for [configName].[name].config.js
by default.path
: To avoid creating another directory, since the service looks for files in config/[configName]
by default.const config = simpleConfig({
name: 'myApp',
});
The name
option is used to generate the default values of multiple options:
defaultConfigFilename
: myApp.config.js
.envVarName
: MY_APP_CONFIG
.path
: config/myApp
.filenameFormat
: myApp.[name].config.js
.In the first example, we went directly to load a configuration file, but the service can also be used with the static default configuration:
const config = simpleConfig({
defaultConfig: {
foo: 'bar',
},
});
const foo = config.get<string>('foo');
Since the service can hold many configurations, you can also specify the name of the default one with the defaultConfigName
option:
const config = simpleConfig({
defaultConfigName: 'fooBar',
defaultConfig: {
foo: 'bar',
},
});
And, if you want the default configuration to be loaded from a specific file, you can specify its name with the defaultConfigFilename
option:
const config = simpleConfig({
defaultConfigFilename: 'fooBar.config.js',
});
// ...
await config.loadFromFile();
In the first example, we were using the value of NODE_ENV
to decide which configuration to load, but we could also use a specific env var to tell the service which configuration to load:
APP_CONFIG=development node app
Then...
const config = simpleConfig();
// ...
await config.loadFromEnv();
Using the defaults, the service will look at APP_CONFIG
, and try to load config/app.development.config.js
, if it exists.
By default, and using the name
option, the service will look for the files in config/[name]
, relative to the project root; this can be changed with the path
option, by sending another path, also relative to the project root:
const config = simpleConfig({
path: '.config',
defaultConfigFilename: 'config.js',
});
// ...
await config.loadFromFile();
With that, it will look for .config/config.js
instead of config/app/app.config.js
.
While developing an app, or when debugging it, it may be useful to switch between different configurations, and that's why the service gives you the possiblity of enabling this feature:
const config = simpleConfig({
allowConfigSwitch: true,
});
// ...
await config.switch('anotherConfig');
With that, the service will try to switch the active configuration to the one specified, and if it's not loaded, it will try to load it from a file.
The service has another option relevant to this feature, which is the allowConfigSwitchSetting
: When this setting is enabled, which it is by default, whenever the service loads a configuration, it will check if it has a boolean allowConfigSwitch
property, and if it does, it will set the service property to the new value.
When calling the load*
methods, the service ignores the allowConfigSwitch
option, but if the implementation uses .switch
, you can specify allowConfigSwitch: false
on your configuration file, so once the file is loaded, the feature will be disabled.
By default, all the configurations extend from the default one (sent when the service is initialized), but you can include an extends
key on the configurations with the name of the one you want to extend.
Whenever the service tries to load a configuration that extends from one that is not registered, it will attempt to load it from a file.
The service is not only about managing files, it also comes with utility methods to read and write on the configurations:
Get a single setting:
const value = config.get('some-setting');
Get multiple settings:
const { settingOne, settingTwo } = config.get(['settingOne', 'settingTwo']);
Get multiple settings as an array:
const [settingOne, settingTwo] = config.get(['settingOne', 'settingTwo'], true);
And since the service uses my own object-utils
, you can also use the get
method with a dot-notation:
const value = config.get('some.setting');
// or
const [settingOne, settingTwo] = config.get(
['one.group.setting', 'two.something.else'],
true,
);
Write a single setting:
config.set('some-setting', 'some-value');
Write multiple settings:
config.set({
settingOne: 'valueOne',
settingTwo: 'valueTwo',
});
And, like get
, you can use dot notation:
config.set('some.setting', 'some-value');
// or
config.set({
'one.group.setting': 'valueOne',
'two.something.else': 'valueTwo',
});
A special feature of the set
method is that, by default, if the new value and the old values are both objects, instead of overwriting the old one, it will deep merge the new one into the old one:
config.set('person', {
name: 'Rosario',
birthday: '',
});
// ...
config.set('person', {
birthday: '25-09-2015',
});
/**
* The current value of the setting would be:
* {
* name: 'Rosario',
* birthday: '25-09-2015',
* }
*/
You can disable this functionality by sending a third parameter to the set
method:
config.set(
'person',
{
birthday: '25-09-2015',
},
false,
);
/**
* The current value of the setting would be:
* {
* birthday: '25-09-2015',
* }
*/
If your app uses a Jimple container, you can register SimpleConfig
as the config
service by using its provider:
import { simpleConfigProvider } from '@homer0/simple-config';
// ...
container.register(simpleConfigProvider);
// ...
const config = container.get('config');
And since the provider is a "provider creator" (created with my custom version of Jimple), you can customize its service name, and options:
container.register(
simpleConfigProvider({
serviceName: 'myConfig',
path: '.config',
defaultConfigFilename: 'config.js',
}),
);
SimpleConfig
depends on the following services, and when used with Jimple, it will try to find them in the container, otherwise, it will create new instances:
@homer0/env-utils
, with the name envUtils
. Used to get the check the environment variable.@homer0/root-file
, with the name rootFile
. Used to get import the configuration files.@homer0/path-utils
, with the name pathUtils
. Needed by rootFile
to generate the paths relative to the project root.If you already implement the dependencies, but with a different name, you can specify them in the provider:
container.register(
simpleConfigProvider({
services: {
envUtils: 'myEnvUtils',
rootFile: 'myRootFile',
pathUtils: 'myPathUtils',
},
}),
);
As this project is part of the packages
monorepo, some of the tooling, like lint-staged
and husky
, are installed on the root's package.json
.
Task | Description |
---|---|
lint | Lints the package. |
test | Runs the unit tests. |
build | Transpiles the project. |
types:check | Validates the TypeScript types. |
FAQs
Simple configuration management for your projects.
We found that @homer0/simple-config demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.